package roborally.supporting;

import be.kuleuven.cs.som.annotate.Basic;
import be.kuleuven.cs.som.annotate.Value;
/**
 * An enum representing the different orientations (up, right, down, left).
 * 
 * @invar 
 * 		| isProperOrientations()
 * 
 * @author Molderez Tom, 2de bach Ingenieurswetenschappen Computerwetenschappen-Elektrotechniek and Reniers Jorn, 2de bach Ingenieurswetenschappen Werktuigkunde-Computerwetenschappen
 * @version 2.0
 */
@Value
public enum Orientation {
	UP(0), RIGHT(1), DOWN(2), LEFT(3);
	/**
	 * Creates a new orientation.
	 * 
	 * @param orientation The integer representing this orientation (zero is up, 1 is right, 2 is down and 3 is left)
	 * @post 
	 * 		| (new this).getValue() == (Math.abs(orientation))%4
	 */
	private Orientation(int orientation) {
		this.orientation = (Math.abs(orientation)) % 4;
	}
	
	/**
	 * Checks whether the orientations are valid.
	 * 
	 * @return 
	 * 		| for each index in {0,1,2,3} 
	 * 		|	if Orientation.values()[index].getOrientation() != index
	 * 		|		then result == false
	 * 		| result == true
	 */
	public static boolean isProperOrientations() {
		for(int index = 0; index < Orientation.values().length; index++)
			if(Orientation.values()[index].getOrientation() != index)
				return false;
		return true;
	}
		
	/**
	 * Returns the integer representing this orientation, zero is up, 1 is right, 2 is down and 3 is left.
	 */
	@Basic
	public int getOrientation() {
		return this.orientation;
	}
	
	/**
	 * The integer representing this orientation, zero is up, 1 is right, 2 is down and 3 is left.
	 */
	private final int orientation;
	
	/**
	 * Returns the orientation if this orientation turns 90 degrees according to direction.
	 * 
	 * @param direction The direction in which this orientation should turn.
	 * @return 
	 * 		| if direction != null
	 * 		|	then result == Orientation.values()[ (this.getValue() + direction.getDirection() + 4) % 4 ]
	 *		| else
	 * 		| 	result == this
	 */
	public Orientation turn(Direction direction){
		if (direction != null)
			return Orientation.values()[ (this.getOrientation() + direction.getDirection() + 4) % 4 ];
		else 
			return this;
	}
	
	/**
	 * Returns the orientation if this orientation turns  steps times 90 degrees according to the sign of steps, positive turns clockwise, negative turns counterclockwise.
	 * 
	 * @param steps The number of times this orientation should turn 90 degrees according to the sign of steps, positive turns clockwise, negative turns counterclockwise.
	 * @return
	 * 		| result == Orientation.values()[ (this.getValue() + (steps%4) + 4)%4];
	 */
	public Orientation turn(int steps){
		return Orientation.values()[ (this.getOrientation() + (steps%4) + 4)%4];
	}

	/**
	 * Controls whether this orientation and the other represent opposite direction (up and down or left and right).
	 * 
	 * @param orientation The orientation to compare this to.
	 * @return 
	 * 		| if(orientation == null)
	 * 		|	result == false;
	 * 		| else
	 * 		|	result == ( 2 == Math.abs(this.getValue() - orientation.getValue()) )
	 */
	public boolean oppositeOrientation(Orientation orientation) {
		if(orientation == null)
			return false;
		return 2 == Math.abs(this.getOrientation() - orientation.getOrientation());				
	}
	
	/**
	 * Returns a textual representation of this.
	 * 
	 * @return A textual representation of this.
	 * 		| if this.getOrientation() == 0
	 * 		| 	then result == this.getClass().getSimpleName() + ": up(" + getOrientation() + ")"
	 * 		| else if this.getOrientation() == 1
	 * 		|	then result == this.getClass().getSimpleName() + ": right(" + getOrientation() + ")"
	 * 		| else if this.getOrientation() == 2
	 * 		|	then result == this.getClass().getSimpleName() + ": down(" + getOrientation() + ")"
	 * 		| else if this.getOrientation() == 3
	 * 		|	then result == this.getClass().getSimpleName() + ": left(" + getOrientation() + ")"
	 * 		| else
	 * 		|  	then result == ""
	 */
	@Override
	public String toString() {
		int orientation = this.getOrientation();
		switch(orientation) {
			case 0: return this.getClass().getSimpleName() + ": up(" + getOrientation() + ")";
			case 1: return this.getClass().getSimpleName() + ": right(" + getOrientation() + ")";
			case 2: return this.getClass().getSimpleName() + ": down(" + getOrientation() + ")";
			case 3: return this.getClass().getSimpleName() + ": left(" + getOrientation() + ")";
			default: return "";
		}
	}
}
